package cn.kgc;

import cn.kgc.grace.result.GraceJSONResult;
import cn.kgc.grace.result.ResponseStatusEnum;
import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPathPredicateItem;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPredicateItem;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.GatewayApiDefinitionManager;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;

import java.util.HashSet;
import java.util.Set;


/**
 * @author k
 */
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@EnableDiscoveryClient
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    /**
     * 用来返回限流的错误信息
     */
    private static void initBlockHandler() {
        // 自定义异常结果
        GatewayCallbackManager.setBlockHandler(
                (exchange, throwable) -> {
                    // 封装错误信息
                    GraceJSONResult graceJsonResult = GraceJSONResult.errorCustom(ResponseStatusEnum.IP_BLACK_ERROR);
                    return ServerResponse.status(HttpStatus.TOO_MANY_REQUESTS)
                            .contentType(MediaType.APPLICATION_JSON)
                            .body(Mono.just(graceJsonResult), GraceJSONResult.class);
                });
    }

    /**
     * 给不同的分组设置不同的流控规则
     */
    private static void initGatewayRuleApi() {
        //初始化API请求分组
        initCustomizedApis();
        //初始化限流规则
        Set<GatewayFlowRule> rules = new HashSet<>();
        GatewayFlowRule rule = new GatewayFlowRule();
        // API资源类型 route: RESOURCE_MODE_ROUTE_ID 0,   api分组：RESOURCE_MODE_CUSTOM_API_NAME 1
        rule.setResourceMode(SentinelGatewayConstants.RESOURCE_MODE_CUSTOM_API_NAME);
        // 资源名称，一般设置为网关中的route名称或者用户自定义的API分组名称
        rule.setResource("service-work");
        // 阈值类型     QPS:FLOW_GRADE_QPS    线程数:FLOW_GRADE_THREAD
        rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        // QPS阈值
        rule.setCount(1);
        // 统计时间窗口，单位是秒，默认是 1 秒
        rule.setIntervalSec(1);
        // 流量的控制效果  目前支持快速失败 CONTROL_BEHAVIOR_DEFAULT 和匀速排队CONTROL_BEHAVIOR_RATE_LIMITER
        rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT);
        // 应对突发请求时额外允许的请求数目，默认为int类型 0
        rule.setBurst(0);
        rules.add(rule);
        GatewayRuleManager.loadRules(rules);
    }

    /**
     * 根据请求路径不同，初始化API请求分组
     */
    private static void initCustomizedApis() {
        // 创建API定义集合
        Set<ApiDefinition> apiDefinitions = new HashSet<>();
        // 定义简历相关接口API
        Set<ApiPredicateItem> resumePredicateItems = new HashSet<>();
        resumePredicateItems.add(
                new ApiPathPredicateItem()
                        .setPattern("/resume/**")
                        .setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX)
        );
        ApiDefinition resumeApi = new ApiDefinition("resume_api").setPredicateItems(resumePredicateItems);
        // 定义用户相关接口API
        Set<ApiPredicateItem> userPredicateItems = new HashSet<>();
        userPredicateItems.add(
                new ApiPathPredicateItem()
                        .setPattern("/user/**")
                        .setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX)
        );
        ApiDefinition userApi = new ApiDefinition("user_api").setPredicateItems(userPredicateItems);
        // 添加到集合并加载
        apiDefinitions.add(resumeApi);
        apiDefinitions.add(userApi);
        GatewayApiDefinitionManager.loadApiDefinitions(apiDefinitions);
    }

}
